package com.ecoolex.ying.space.core.manager;

import com.ecoolex.framework.common.enums.ResultCode;
import com.ecoolex.framework.common.util.BigDecimalUtil;
import com.ecoolex.framework.common.util.BizAssert;
import com.ecoolex.framework.common.util.Check;
import com.ecoolex.framework.common.util.DateUtil;
import com.ecoolex.framework.common.util.MessageFormatter;
import com.ecoolex.framework.modularize.api.auth.user.ConsumerMemberManager;
import com.ecoolex.framework.modularize.api.auth.user.ConsumerWeiXinMember;
import com.ecoolex.ying.space.core.dao.PaymentOrderDAO;
import com.ecoolex.ying.space.core.external.WeiXinPayExternalService;
import com.ecoolex.ying.space.entity.EstateProjectActivityCouponOrder;
import com.ecoolex.ying.space.entity.PaymentOrder;
import com.ecoolex.ying.space.entity.PaymentOrder.Column;
import com.ecoolex.ying.space.entity.PaymentOrderExample;
import com.ecoolex.ying.space.entity.PaymentOrderExample.Criteria;
import com.ecoolex.ying.space.entity.PaymentOrderRecord;
import com.ecoolex.ying.space.enums.PaymentOrderRecordOperType;
import com.ecoolex.ying.space.enums.PaymentOrderRecordResultStatus;
import com.ecoolex.ying.space.enums.PaymentOrderStatus;
import com.github.binarywang.wxpay.bean.WxPayApiData;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyResult;
import com.github.binarywang.wxpay.bean.result.WxPayOrderQueryResult;
import com.github.binarywang.wxpay.bean.result.WxPayRefundQueryResult;
import com.github.binarywang.wxpay.bean.result.WxPayRefundResult;
import com.github.binarywang.wxpay.exception.WxPayException;

import java.math.BigDecimal;
import java.time.Instant;
import java.util.Date;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service
public class PaymentOrderManager {
	
	private static final Logger log = LoggerFactory.getLogger(PaymentOrderManager.class);
	
    @Autowired
    private PaymentOrderDAO dao;
    
    @Autowired
    private WeiXinPayExternalService weiXinPayExternalService;
    
    @Autowired
    private ConsumerMemberManager consumerMemberManager;
    
    @Autowired
    private PaymentOrderRecordManager paymentOrderRecordManager;
    
    @Autowired
    private EstateProjectActivityCouponOrderManager estateProjectActivityCouponOrderManager;
    
    @Autowired
    private EstateProjectActivityCouponPackageManager packageManager;
    
    private static final String TYPE_ESTATE_PROJECT_ACTIVITY_COUPON_ORDER = "EPACO";
    
    private static final String PAYMENT_TYPE = "WeiXinPay";
    
    @Value("${weixin.pay.app-id}")
    private String appId;
    
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public Map paymentApply(EstateProjectActivityCouponOrder order, String ipAdress, String createUser) {
    	PaymentOrder paymentOrder = getByBizTypeAndOrderNo(TYPE_ESTATE_PROJECT_ACTIVITY_COUPON_ORDER,order.getOrderNo());
    	if(Check.isNull(paymentOrder)) {
    		//创建支付订单
    		paymentOrder = new PaymentOrder();
    		paymentOrder.setBizType(TYPE_ESTATE_PROJECT_ACTIVITY_COUPON_ORDER);
    		paymentOrder.setOrderNo(order.getOrderNo());
    		paymentOrder.setPaymentType(PAYMENT_TYPE);
    		paymentOrder.setPaymentCurrencyType("RMB");
    		paymentOrder.setUserId(order.getUserId());
    		paymentOrder.setPaymentPrice(order.getRealPrice());
    		paymentOrder.setStatus(PaymentOrderStatus.PAY_APPLY.real());
    		paymentOrder.setDeleted(PaymentOrder.NOT_DELETED);
    		paymentOrder.setCreateUser(createUser);
    		paymentOrder.setCreateTime(Date.from(Instant.now()));
    		paymentOrder.setUpdateUser(createUser);
    		paymentOrder.setUpdateTime(Date.from(Instant.now()));
            dao.insert(paymentOrder);
    	}else {
    		//失败才能重新发起支付
    		BizAssert.isTrue(paymentOrder.getStatus().equals(PaymentOrderStatus.PAY_FAILED.real()),
    			ResultCode.BIZ.build(10), "订单支付中或已支付,无法发起支付");
    		paymentOrder.setStatus(PaymentOrderStatus.PAY_APPLY.real());
    		paymentOrder.setUpdateUser(createUser);
    		paymentOrder.setUpdateTime(Date.from(Instant.now()));
    		dao.updateByPrimaryKeySelective(paymentOrder, Column.status,Column.updateUser,Column.updateTime);
    	}
    	//记录支付日志
    	PaymentOrderRecord log = paymentOrderRecordManager.create(paymentOrder,PAYMENT_TYPE, PaymentOrderRecordOperType.ORDER_APPLY);
    	log.setTransAmount(order.getRealPrice());
    	try {
	    	String orderSn = paymentOrder.getBizType() + '-'
	            + paymentOrder.getOrderNo();
	    	ConsumerWeiXinMember weixin = consumerMemberManager.getWeiXinByUserIdAndAppId(order.getUserId(), appId);
	        BizAssert.isTrue(Check.notNull(weixin), ResultCode.BIZ.build(11), "用户未绑定微信, 无法使用微信支付");
	        Map result = weiXinPayExternalService.prepay(weixin.getOpenId(), 
	        								orderSn,
	        								MessageFormatter.arrayFormat("订单：{}", order.getOrderNo()), 
	        								null, 
	        								BigDecimalUtil.dollarToCent(order.getRealPrice()).intValue(), 
	        								ipAdress);
	        log.setResultStatus(PaymentOrderRecordResultStatus.SUCCEED.real());
	        return result;
    	}catch (Exception e) {
    		log.setResultStatus(PaymentOrderRecordResultStatus.FAILED.real());
    		log.setErrorMessage(e.getMessage());
    		//修改支付订单为支付失败
    		paymentOrder.setStatus(PaymentOrderStatus.PAY_FAILED.real());
    		paymentOrder.setUpdateUser(createUser);
    		paymentOrder.setUpdateTime(Date.from(Instant.now()));
    		dao.updateByPrimaryKeySelective(paymentOrder, Column.status,Column.updateUser,Column.updateTime);
    		PaymentOrderManager.log.error("发起支付失败",e);
    		BizAssert.throwBizException("发起支付失败");
    		return null;
		}finally {
			WxPayApiData wxApiData = weiXinPayExternalService.getWxApiData();
			if(Check.notNull(wxApiData)) {
				log.setRequest(wxApiData.getRequestData());
                log.setResponse(wxApiData.getResponseData());
                log.setErrorMessage(wxApiData.getExceptionMsg());
			}
			paymentOrderRecordManager.add(log, createUser);
		}
    }
    
    public String orderNotify(String body, String createUser) {
    	try {
			WxPayOrderNotifyResult notifyResult = weiXinPayExternalService.parseOrderNotifyResult(body);
			//拿到微信返回的订单号
			String outTradeNo = notifyResult.getOutTradeNo();
			int index = outTradeNo.indexOf("-");
			String bizType = outTradeNo.substring(0,index);
			String orderNo = outTradeNo.substring(index+1);
			PaymentOrder paymentOrder = getByBizTypeAndOrderNo(bizType,orderNo);
			BizAssert.isTrue(Check.notNull(paymentOrder),ResultCode.VERIFY.build(10), "支付订单不存在");
			//检查订单支付状态，并且记录操作日志
			PaymentOrderRecord log = paymentOrderRecordManager.create(paymentOrder,PAYMENT_TYPE, PaymentOrderRecordOperType.ORDER_NOTIFY);
			try {
				weiXinPayExternalService.checkNotifyResult(notifyResult);
				log.setTransAmount(BigDecimalUtil.centToDollar(BigDecimal.valueOf(notifyResult.getTotalFee())));
				log.setTransNo(notifyResult.getTransactionId());
				log.setTransTime(DateUtil.parseToDate("yyyyMMddHHmmss", notifyResult.getTimeEnd()));
				log.setPaymentType(PAYMENT_TYPE);
				log.setResultStatus(PaymentOrderRecordResultStatus.SUCCEED.real());
			}catch (Exception e) {
				log.setResultStatus(PaymentOrderRecordResultStatus.FAILED.real());
				log.setErrorMessage(e.getMessage());
			}finally {
				log.setRequest(body);
				log.setResponse(weiXinPayExternalService.notifyResult(true, "通知成功"));
				paymentOrderRecordManager.add(log, createUser);
			}
			updatePaymentOrderAndBizOrder(paymentOrder,log,createUser);
			return log.getResponse();
		} catch (WxPayException e) {
			return weiXinPayExternalService.notifyResult(false, e.getMessage());
		}
    }
    
    public List<PaymentOrder> queryPayApplyTimeout(Date timeout,int size){
    	PaymentOrderExample example = new PaymentOrderExample();
    	Criteria criteria = example.createCriteria();
    	criteria.andStatusEqualTo(PaymentOrderStatus.PAY_APPLY.real())
    			.andUpdateTimeLessThanOrEqualTo(timeout)
    			.andDeletedEqualTo(PaymentOrder.NOT_DELETED);
    	example.orderBy(Column.updateTime.asc());
    	example.limit(size);
    	return dao.selectByExample(example);
    }
    
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public PaymentOrder checkPayStatus(PaymentOrder paymentOrder,String updateUser) {
    	//不是支付中的数据
    	if(PaymentOrderStatus.PAY_APPLY.notEqual(paymentOrder.getStatus())) {
    		paymentOrder.setUpdateUser(updateUser);
    		paymentOrder.setUpdateTime(Date.from(Instant.now()));
            dao.updateByPrimaryKeySelective(paymentOrder,Column.updateUser,Column.updateTime);
            return paymentOrder;
    	}
    	try {
			WxPayOrderQueryResult payResult = weiXinPayExternalService.getPayResult(paymentOrder.getBizType()+"-"+paymentOrder.getOrderNo());
			// 检查支付订单的状态, 并生成相应记录
            PaymentOrderRecord log = paymentOrderRecordManager.create(paymentOrder, PAYMENT_TYPE,PaymentOrderRecordOperType.ORDER_CHECK);
            log.setTransAmount(BigDecimalUtil.centToDollar(BigDecimal.valueOf(payResult.getTotalFee())));
            log.setTransNo(payResult.getTransactionId());

            if (payResult.getTradeState().equalsIgnoreCase("USERPAYING")) {
                // 用户支付中
                log.setResultStatus(PaymentOrderRecordResultStatus.ONGOING.real());
            } else if (payResult.getTradeState().equalsIgnoreCase("SUCCESS")) {
                // 用户支付成功
                log.setTransTime(DateUtil.parseToDate("yyyyMMddHHmmss", payResult.getTimeEnd()));
                log.setResultStatus(PaymentOrderRecordResultStatus.SUCCEED.real());
            } else {
                // 其他原因全为失败
                log.setResultStatus(PaymentOrderRecordResultStatus.FAILED.real());
            }

            WxPayApiData data = weiXinPayExternalService.getWxApiData();
            if (Check.notNull(data)) {
                log.setRequest(data.getRequestData());
                log.setResponse(data.getResponseData());
                log.setErrorMessage(data.getExceptionMsg());
            }
            // 记录调用详情
            paymentOrderRecordManager.add(log, updateUser);

            updatePaymentOrderAndBizOrder(paymentOrder, log, updateUser);
		} catch (WxPayException e) {
			PaymentOrderManager.log.error("查询订单异常"+e);
		}
    	return paymentOrder;
    }
    
    public void updatePaymentOrderAndBizOrder(PaymentOrder paymentOrder,PaymentOrderRecord log,String updateUser) {
    	//支付成功
    	if(PaymentOrderRecordResultStatus.SUCCEED.real().equals(log.getResultStatus())) {
    		paymentOrder.setPaymentNo(log.getTransNo());
    		paymentOrder.setPaymentTime(log.getTransTime());
    		paymentOrder.setStatus(PaymentOrderStatus.PAY_SUCCEED.real());
    		paymentOrder.setUpdateUser(updateUser);
    		paymentOrder.setUpdateTime(Date.from(Instant.now()));
    		dao.updateByPrimaryKeySelective(paymentOrder, Column.paymentNo,
    													  Column.paymentTime,
    													  Column.status,
    													  Column.updateUser,
    													  Column.updateTime);
    		
    	}else {//支付失败
    		paymentOrder.setStatus(PaymentOrderStatus.PAY_FAILED.real());
    		paymentOrder.setUpdateUser(updateUser);
    		paymentOrder.setUpdateTime(Date.from(Instant.now()));
    		dao.updateByPrimaryKeySelective(paymentOrder, Column.status,Column.updateUser,Column.updateTime);
    	}
    	//通知业务订单
    	if(paymentOrder.getBizType().equals(TYPE_ESTATE_PROJECT_ACTIVITY_COUPON_ORDER)) {
    		estateProjectActivityCouponOrderManager.paySuccess(paymentOrder, updateUser);
    	}
    }
    
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public boolean refundApply(EstateProjectActivityCouponOrder order,String updateUser) {
    	PaymentOrder paymentOrder = getByBizTypeAndOrderNo(TYPE_ESTATE_PROJECT_ACTIVITY_COUPON_ORDER,order.getOrderNo());
    	BizAssert.isTrue(Check.notNull(paymentOrder),ResultCode.BIZ.build(10), "未找到支付订单，无法发起退款");
    	// 已支付或失败才能发起
        BizAssert.isTrue(PaymentOrderStatus.PAY_SUCCEED.equal(paymentOrder.getStatus())
            || PaymentOrderStatus.REFUND_FAILED.equal(paymentOrder.getStatus()), ResultCode.BIZ.build(11), "订单必须已支付, 才能发起退款");
        // 变更状态为发起退款
        paymentOrder.setStatus(PaymentOrderStatus.REFUND_APPLY.real());
        paymentOrder.setUpdateUser(updateUser);
        paymentOrder.setUpdateTime(Date.from(Instant.now()));
        dao.updateByPrimaryKeySelective(paymentOrder, Column.status,Column.updateUser,Column.updateTime);
        
        // 创建日志
        PaymentOrderRecord log = paymentOrderRecordManager.create(paymentOrder,PAYMENT_TYPE, PaymentOrderRecordOperType.REFUND_APPLY);
        
        try {
			WxPayRefundResult refundResult = weiXinPayExternalService.refund(paymentOrder.getBizType()+"-"+paymentOrder.getOrderNo(),
											"r-"+paymentOrder.getBizType()+"-"+paymentOrder.getOrderNo(), 
											null, 
											paymentOrder.getPaymentPrice(), 
											paymentOrder.getPaymentPrice());
			log.setTransAmount(BigDecimalUtil.centToDollar(BigDecimal.valueOf(refundResult.getRefundFee())));
			log.setTransNo(refundResult.getRefundId());
			log.setResultStatus(PaymentOrderRecordResultStatus.SUCCEED.real());
			
		} catch (WxPayException e) {
			log.setResultStatus(PaymentOrderRecordResultStatus.FAILED.real());
			log.setErrorMessage(e.getMessage());
			paymentOrder.setStatus(PaymentOrderStatus.REFUND_FAILED.real());
			paymentOrder.setUpdateUser(updateUser);
			paymentOrder.setUpdateTime(Date.from(Instant.now()));
			dao.updateByPrimaryKeySelective(paymentOrder, Column.status,Column.updateUser,Column.updateTime);
		}finally {
			WxPayApiData data = weiXinPayExternalService.getWxApiData();
			if(Check.notNull(data)) {
				log.setRequest(data.getRequestData());
                log.setResponse(data.getResponseData());
                log.setErrorMessage(data.getExceptionMsg());
			}
			//调用记录日志
			paymentOrderRecordManager.add(log, updateUser);
		}
        if(PaymentOrderRecordResultStatus.SUCCEED.equal(log.getResultStatus())) {
        	//通知业务订单
        	if(paymentOrder.getBizType().equals(TYPE_ESTATE_PROJECT_ACTIVITY_COUPON_ORDER)) {
        		estateProjectActivityCouponOrderManager.refundSuccess(paymentOrder, updateUser);
        	}
        	return true;
        }
        return false;
    }
    
    public String refundNotify(String body, String createUser) {
    	try {
			WxPayRefundNotifyResult notifyResult = weiXinPayExternalService.parseRefundNotifyResult(body);
			String outTradeNo = notifyResult.getReqInfo().getOutTradeNo();
			int index = outTradeNo.indexOf('-');
            String bizType = outTradeNo.substring(0, index);
            String orderNo = outTradeNo.substring(index + 1);
            PaymentOrder paymentOrder = getByBizTypeAndOrderNo(bizType,orderNo);
            BizAssert.isTrue(Check.notNull(paymentOrder), "支付订单不存在");
            
            PaymentOrderRecord log = paymentOrderRecordManager.create(paymentOrder,PAYMENT_TYPE, PaymentOrderRecordOperType.REFUND_NOTIFY);
            try {
			    log.setTransAmount(BigDecimalUtil.centToDollar(BigDecimal.valueOf(notifyResult.getReqInfo().getRefundFee())));
			    log.setTransNo(notifyResult.getReqInfo().getRefundId());
			    if(notifyResult.getReqInfo().getRefundStatus().equalsIgnoreCase("SUCCESS")) {
			    	log.setResultStatus(PaymentOrderRecordResultStatus.SUCCEED.real());
			    	log.setTransTime(DateUtil.parseToDate(DateUtil.DEFAULT_DATETIME_FORMAT,
			                notifyResult.getReqInfo().getSuccessTime()));
			    }else {
			    	log.setResultStatus(PaymentOrderRecordResultStatus.FAILED.real());
			    }
			    paymentOrder.setPaymentNo(log.getTransNo());
			    paymentOrder.setPaymentTime(log.getTransTime());
            }catch (Exception e) {
            	log.setResultStatus(PaymentOrderRecordResultStatus.FAILED.real());
            	log.setErrorMessage(e.getMessage());
            }finally {
            	// 记录相应的日志
            	log.setRequest(notifyResult.toString());
            	log.setResponse(weiXinPayExternalService.notifyResult(true, "通知成功"));
            	paymentOrderRecordManager.add(log, createUser);
            }
            //更新退款单
            updateRefundStatusAndNotify(paymentOrder,log,createUser);
            return log.getResponse();
		} catch (WxPayException e) {
			return weiXinPayExternalService.notifyResult(false, e.getMessage());
		}
    }
    
    public List<PaymentOrder> queryRefundApplyTimeout(Date timeout, int size) {
        PaymentOrderExample example = new PaymentOrderExample();
        example.createCriteria().andStatusEqualTo(PaymentOrderStatus.REFUND_APPLY.real()).andDeletedEqualTo(
            PaymentOrder.NOT_DELETED).andUpdateTimeLessThanOrEqualTo(timeout);
        example.orderBy(Column.updateTime.asc());
        example.limit(size);
        return dao.selectByExample(example);
    }

    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public PaymentOrder checkRefundStatus(PaymentOrder paymentOrder, String updateUser) {
        // 不是退款中的数据,直接不检查
        if (PaymentOrderStatus.REFUND_APPLY.notEqual(paymentOrder.getStatus())) {
            return paymentOrder;
        }
        try {
            WxPayRefundQueryResult result = weiXinPayExternalService.getRefundResult("r-" + paymentOrder.getBizType()
                + '-'
                + paymentOrder.getOrderNo());

            result.getRefundRecords().forEach(record -> {
                // 检查支付订单的状态, 并生成相应记录
                PaymentOrderRecord log = paymentOrderRecordManager.create(paymentOrder,PAYMENT_TYPE, PaymentOrderRecordOperType.REFUND_CHECK);

                log.setTransAmount(BigDecimalUtil.centToDollar(BigDecimal.valueOf(record.getRefundFee())));
                log.setTransNo(record.getRefundId());

                if (record.getRefundStatus().equalsIgnoreCase("PROCESSING")) {
                    // 用户支付中
                    log.setResultStatus(PaymentOrderRecordResultStatus.ONGOING.real());
                } else if (record.getRefundStatus().equalsIgnoreCase("SUCCESS")) {
                    // 用户支付成功
                    log.setTransTime(
                        DateUtil.parseToDate(DateUtil.DEFAULT_DATETIME_FORMAT, record.getRefundSuccessTime()));
                    log.setResultStatus(PaymentOrderRecordResultStatus.SUCCEED.real());
                } else {
                    // 其他原因全为失败
                    log.setResultStatus(PaymentOrderRecordResultStatus.FAILED.real());
                }

                WxPayApiData data = weiXinPayExternalService.getWxApiData();
                if (Check.notNull(data)) {
                    log.setRequest(data.getRequestData());
                    log.setResponse(data.getResponseData());
                    log.setErrorMessage(data.getExceptionMsg());
                }
                // 记录调用详情
                paymentOrderRecordManager.add(log, updateUser);

                updateRefundStatusAndNotify(paymentOrder, log, updateUser);
            });
        } catch (Exception e) {
            log.error("查询订单异常", e);
        }

        return paymentOrder;
    }
    
    public void updateRefundStatusAndNotify(PaymentOrder paymentOrder,PaymentOrderRecord log,String updateUser) {
    	if(PaymentOrderRecordResultStatus.SUCCEED.equal(log.getResultStatus())) {
    		paymentOrder.setRefundPrice(log.getTransAmount());
    		paymentOrder.setStatus(PaymentOrderStatus.REFUND_SUCCEED.real());
    		paymentOrder.setUpdateUser(updateUser);
			paymentOrder.setUpdateTime(Date.from(Instant.now()));
			dao.updateByPrimaryKeySelective(paymentOrder, Column.refundPrice,Column.status,Column.updateUser,Column.updateTime);
    	}else {
    		paymentOrder.setStatus(PaymentOrderStatus.REFUND_FAILED.real());
    		paymentOrder.setUpdateUser(updateUser);
			paymentOrder.setUpdateTime(Date.from(Instant.now()));
			dao.updateByPrimaryKeySelective(paymentOrder,Column.status,Column.updateUser,Column.updateTime);
    	}
    }
    
    public PaymentOrder getByBizTypeAndOrderNo(String bizType, String orderNo) {
    	PaymentOrderExample example = new PaymentOrderExample();
    	Criteria criteria = example.createCriteria();
    	criteria.andBizTypeEqualTo(bizType).andOrderNoEqualTo(orderNo).andDeletedEqualTo(PaymentOrder.NOT_DELETED);
    	return dao.selectOneByExample(example);
    }
}